package com.tianji.learning.service.impl;

import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.domain.query.PointsBoardQuery;
import com.tianji.learning.domain.vo.PointsBoardItemVO;
import com.tianji.learning.domain.vo.PointsBoardVO;
import com.tianji.learning.mapper.PointsBoardMapper;
import com.tianji.learning.service.IPointsBoardService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 学霸天梯榜 服务实现类
 * </p>
 *
 * @author author
 * @since 2025-03-28
 */
@Service
@RequiredArgsConstructor
public class PointsBoardServiceImpl extends ServiceImpl<PointsBoardMapper, PointsBoard> implements IPointsBoardService {

    private final StringRedisTemplate redisTemplate;
    private final UserClient userClient;
    /**
     * 查询积分榜单
     *
     * @param pointsBoardQuery
     * @return
     */
    @Override
    public PointsBoardVO getBoard(PointsBoardQuery pointsBoardQuery) {
        Long season = pointsBoardQuery.getSeason();
        boolean isCurrent = season == 0 || season == null;
        LocalDateTime now = LocalDateTime.now();
        String key = RedisConstants.POINTS_BOARD_KEY_PREFIX + now.format(DateUtils.POINTS_BOARD_SUFFIX_FORMATTER);
        // 查询我的积分和排名
        PointsBoard myBoard = isCurrent ?
                queryMyCurrentBoard(key) :
                queryMyHistoryBoard(season);
        // 查询榜单列表
        List<PointsBoardItemVO> boardList = isCurrent ?
                queryCurrentBoard(key, pointsBoardQuery.getPageNo(), pointsBoardQuery.getPageSize()) :
                queryHistoryBoard(pointsBoardQuery);

        PointsBoardVO pointsBoardVO = new PointsBoardVO();
        if (myBoard != null) {
            pointsBoardVO.setRank(myBoard.getRank());
            pointsBoardVO.setPoints(myBoard.getPoints());
        }

        pointsBoardVO.setBoardList(boardList);
        return pointsBoardVO;
    }

    /**
     * 根据赛季id创建积分榜单
     *
     * @param season
     */
    @Override
    public void createPointsBoardTableBySeason(Integer season) {
        getBaseMapper().createPointsBoardTable("points_board_" + season);
    }

    private List<PointsBoardItemVO> queryHistoryBoard(PointsBoardQuery pointsBoardQuery) {
        return null;
    }

    /**
     * 查询当前榜单
     * @param key
     * @param pageNo
     * @param pageSize
     * @return
     */
    @Override
    public List<PointsBoardItemVO> queryCurrentBoard(String key, Integer pageNo, Integer pageSize) {
        // 计算分页
        int from = (pageNo - 1) * pageSize;
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet()
                .reverseRangeWithScores(key, from, from + pageSize - 1);
        if (CollUtils.isEmpty(typedTuples)) {
            return CollUtils.emptyList();
        }
        // 封装
        int rank = from + 1;
        List<PointsBoardItemVO> pb = new ArrayList<>();
        List<Long> ids = new ArrayList<>();

        for (ZSetOperations.TypedTuple<String> item : typedTuples) {
            ids.add(Long.valueOf(item.getValue()));
        }
        List<UserDTO> userDTOList = userClient.queryUserByIds(ids);
        Map<Long, UserDTO> userMp = userDTOList.stream().collect(Collectors.toMap(v->v.getId(), v->v));

        if (ObjectUtils.isEmpty(userMp)) {
            return CollUtils.emptyList();
        }

        for (ZSetOperations.TypedTuple<String> item : typedTuples) {
            PointsBoardItemVO  pbi = new PointsBoardItemVO();
            pbi.setRank(rank++);
            pbi.setPoints(item.getScore().intValue());
            pbi.setName(userMp.get(Long.valueOf(item.getValue())).getUsername());
            pb.add(pbi);
        }

        return pb;
    }

    // 将当前榜单数据持久化到数据库
    @Override
    public List<PointsBoard> saveCurrentBoardToDB(String key, Integer pageNo, Integer pageSize) {
        // 计算分页
        int from = (pageNo - 1) * pageSize;
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet()
                .reverseRangeWithScores(key, from, from + pageSize - 1);
        if (CollUtils.isEmpty(typedTuples)) {
            return CollUtils.emptyList();
        }
        // 封装
        int rank = from + 1;
        List<PointsBoard> pb = new ArrayList<>();
        List<Long> ids = new ArrayList<>();

        for (ZSetOperations.TypedTuple<String> item : typedTuples) {
            ids.add(Long.valueOf(item.getValue()));
        }

        for (ZSetOperations.TypedTuple<String> item : typedTuples) {
            PointsBoard  pbi = new PointsBoard();
            pbi.setRank(rank++);
            pbi.setPoints(item.getScore().intValue());
            pbi.setUserId(Long.valueOf(item.getValue()));
            pb.add(pbi);
        }

        return pb;
    }

    private PointsBoard queryMyHistoryBoard(Long season) {
        return null;
    }

    /**
     * 查询我的积分排名xinxi
     * @param key
     * @return
     */
    private PointsBoard queryMyCurrentBoard(String key) {
        // 1、绑定key
        BoundZSetOperations<String, String> ops = redisTemplate.boundZSetOps(key);
        // 2、查询积分
        String userId = UserContext.getUser().toString();
        // 3、查询成绩
        Double points = ops.score(userId);
        // 4、查询排名
        Long rank = ops.reverseRank(userId);
        // 5、封装返回
        PointsBoard pb = new PointsBoard();
        pb.setPoints(points == null ? 0 : points.intValue());
        pb.setRank(rank == null ? 0 : rank.intValue() + 1);
        return pb;
    }

}
